package com.gta.edu.sdk.log.aspect;

import java.util.Date;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.gta.edu.sdk.base.call.IUserAuthServerCall;
import com.gta.edu.sdk.common.dto.LoginUserInfo;
import com.gta.edu.sdk.common.dto.ResultData;
import com.gta.edu.sdk.log.annotation.OperationLog;
import com.gta.edu.sdk.log.entity.TSysLogInfo;
import com.gta.edu.sdk.util.RedisClusterUtil;
import com.gta.edu.sdk.util.StringUtil;

import javassist.NotFoundException;
import net.sf.json.JSONObject;

@Aspect
@Component
public class LogAspect {
	private static final Logger log = LogManager.getLogger(LogAspect.class);

	@Autowired
	private IUserAuthServerCall userAuthServerCall;
	@Value("${sys-code}")
	private String sysCode; // 从配置文件中读取写入文件的路径信息
	private final String LOG_REDIS_KEY = "OPERATE_LOG_KEY";

	/**
	 * 此处的切点是注解的方式，也可以用包名的方式达到相同的效果 '@Pointcut("execution(*
	 * com.wwj.springboot.service.impl.*.*(..))")'
	 */
	@Pointcut("@annotation(com.gta.edu.sdk.log.annotation.OperationLog)")
	public void operationLog() {
	}

	/**
	 * 环绕增强，相当于MethodInterceptor
	 */
	@Around("operationLog()")
	public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
		Object res = null;
		long time = System.currentTimeMillis();
		try {
			res = joinPoint.proceed();
			time = System.currentTimeMillis() - time;
		} finally {
			try {
				// 方法执行完成后增加日志
				addOperationLog(joinPoint, res, time);
			} catch (Exception e) {
				log.error("写操作日志失败！", e);
			}
		}
		return res;
	}

	private void addOperationLog(JoinPoint joinPoint, Object res, long time) throws ClassNotFoundException, NotFoundException {
		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = requestAttributes.getRequest();
		LoginUserInfo userInfo = this.getLongUserInfo(request);
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		OperationLog annotation = signature.getMethod().getAnnotation(OperationLog.class);
		
		TSysLogInfo log = new TSysLogInfo();
		log.setExeTime(time);
		if (request.getHeader("x-forwarded-for") == null) {
			log.setIp(request.getRemoteAddr());
		} else {
			log.setIp(request.getHeader("x-forwarded-for"));
		}
		if (annotation != null) {
			log.setLogContent(annotation.detail());
			log.setOpType(annotation.operationType().getValue());
			log.setLogType(annotation.logType().getValue());
		}
//		log.setOpEntity(joinPoint.toString());
		if (userInfo != null) {
			log.setOper(userInfo.getIscUserSourceId());
			log.setOperName(userInfo.getIscAdCode());
		}
		log.setOpTime(new Date());
		if (res != null) {
			if (res instanceof ResultData) {
				JSONObject result = JSONObject.fromObject(res);
				if (result.getBoolean("success")) {
					log.setResult(1);
				} else {
					log.setResult(0);
				}
			} else {
				log.setResult(1);
			}
		} else {
			log.setResult(-1);
		}
		log.setSysCode(sysCode);
		String methodParamsData = this.generateParamsData(joinPoint);
		log.setOpEntity(methodParamsData);
		RedisClusterUtil.getInstance().addList(LOG_REDIS_KEY, log);
	}
	
	private String generateParamsData(JoinPoint joinPoint) throws ClassNotFoundException, NotFoundException {
		StringBuilder context = new StringBuilder();
		context.append("执行的方法：").append(joinPoint.toString());
		context.append(",方法中对应的参数数据：");
		Object[] params = joinPoint.getArgs();
		String[] filterParamType = new String[] {"org.apache.catalina.connector.RequestFacade"};
		if (params != null && params.length > 0) {
			boolean isExit = false;
			for (int i=0;i<params.length;i++) {
				isExit = false;
				for (int j=0;j<filterParamType.length;j++) {
					if (params[i] == null || params[i] != null && params[i].toString().contains(filterParamType[j])) {
						isExit = true;
					}
				}
				if (!isExit) {
					context.append("参数" + (i + 1) + ":").append(com.alibaba.fastjson.JSONObject.toJSONString(params[i]));
					if (i != params.length-1) {
						context.append(",");
					}
				}
			}
		}
		return context.toString();
	}
	
	
//	private String operateContent(JoinPoint joinPoint, String methodName) throws ClassNotFoundException, NotFoundException {
//        String className = joinPoint.getTarget().getClass().getName();
//        Object[] params = joinPoint.getArgs();
//        String classType = joinPoint.getTarget().getClass().getName();
//        Class<?> clazz = Class.forName(classType);
//        String clazzName = clazz.getName();
//        Map<String,Object > nameAndArgs = getFieldsName(this.getClass(), clazzName, methodName,params);
////        StringBuffer bf = new StringBuffer();
////        if (!CollectionUtils.isEmpty(nameAndArgs)){
////            Iterator it = nameAndArgs.entrySet().iterator();
////            while (it.hasNext()){
////                Map.Entry entry = (Map.Entry) it.next();
////                String key = (String) entry.getKey();
////                String value = JSONObject.toJSONString(entry.getValue());
////                bf.append(key).append("=");
////                bf.append(value).append("&");
////            }
////        }
////        if (StringUtils.isEmpty(bf.toString())){
////            bf.append(request.getQueryString());
////        }
////        return String.format(LOG_CONTENT, className, methodName, bf.toString(), ip);
//        return null;
//    }
//	
//	private Map<String,Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
//        Map<String,Object > map=new HashMap<String,Object>();
//
//        ClassPool pool = ClassPool.getDefault();
//        ClassClassPath classPath = new ClassClassPath(cls);
//        pool.insertClassPath(classPath);
//
//        CtClass cc = pool.get(clazzName);
//        CtMethod cm = cc.getDeclaredMethod(methodName);
//        MethodInfo methodInfo = cm.getMethodInfo();
//        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
//        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
//        if (attr == null) {
//            // exception
//            return map;
//        }
//        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
//        for (int i = 0; i < cm.getParameterTypes().length; i++){
//            map.put( attr.variableName(i + pos),args[i]);//paramNames即参数名
//        }
//        return map;
//    }

	private LoginUserInfo getLongUserInfo(HttpServletRequest request) {
		String ticket = this.getTicket(request);
		if (StringUtil.isNullOrEmpty(ticket)) {
			log.error("获取登录用户的ticket信息为空！请检查从浏览器的cookie中是否存在token数据");
			return null;
		}
		String loginUserResultStr = this.userAuthServerCall.getLoginUser(ticket);
		LoginUserInfo loginUserInfo = null;
		if (StringUtil.isNullOrEmpty(loginUserResultStr)) {
			log.error("获取登录用户信息为空，当前为未调用用户认证接口，而且也未调用回调函数！");
		} else {
			JSONObject loginUserResultJson = JSONObject.fromObject(loginUserResultStr);
			if (loginUserResultJson != null) {
				if (loginUserResultJson.getBoolean("success")) {
					String loginUserStr = loginUserResultJson.getString("data");
					log.info(loginUserStr);
					loginUserInfo = this.madeLoginUserInfo(loginUserStr);
				} else {
					log.error(loginUserResultJson.getString("errorMsg"));
				}
			} else {
				log.error("获取登录用户信息错误，无法转变为对象信息！");
			}
		}
		return loginUserInfo;
	}

	private String getTicket(HttpServletRequest request) {
		Cookie cookies[] = request.getCookies();
		String ticket = null;
		if (cookies != null) {
			for (Cookie ck : cookies) {
				if (ck.getName().trim().equals("token")) {
					ticket = ck.getValue();
					break;
				}
			}
		}
		return ticket;
	}

	/**
	 * @description 根据登录用户的字符串信息构造成用户对象
	 * @author fengya
	 * @date 2020年4月7日 上午9:08:55
	 * @param loginUserStr 登录用户的json字符串信息
	 * @return LoginUserInfo 登录用户的对象信息
	 */
	private LoginUserInfo madeLoginUserInfo(String loginUserStr) {
		LoginUserInfo loginUserInfo = null;
		if (!StringUtil.isNullOrEmpty(loginUserStr)) {
			JSONObject loginUserJson = JSONObject.fromObject(loginUserStr);
			loginUserInfo = (LoginUserInfo) JSONObject.toBean(loginUserJson, LoginUserInfo.class);
		}
		return loginUserInfo;
	}

	@Before("operationLog()")
	public void doBeforeAdvice(JoinPoint joinPoint) {

	}

	/**
	 * 处理完请求，返回内容
	 * 
	 * @param ret
	 */
	@AfterReturning(returning = "ret", pointcut = "operationLog()")
	public void doAfterReturning(Object ret) {
	}

	/**
	 * 后置异常通知
	 */
	@AfterThrowing("operationLog()")
	public void throwss(JoinPoint jp) {
	}

	/**
	 * 后置最终通知,final增强，不管是抛出异常或者正常退出都会执行
	 */
	@After("operationLog()")
	public void after(JoinPoint jp) {
	}
}
